
<script setup >
// 讯飞聊天接口
// import CryptoJS from "crypto-js";
import { nextTick, ref, watch } from "vue";
// import VueMarkdown from "vue-markdown";
import { ElMessage } from "element-plus";

import { chatHooks } from "./js/chat.js";

import { VMarkdownView } from "vue3-markdown";
import "vue3-markdown/dist/style.css";
const mode = ref("light");

let { text, list, scrollToBottom, disabled, stopAnswer, bigModel } =
  chatHooks();

// 文本框的内容
let message = ref("");
// let contentFlag = ref(false);

// 发送聊天消息

// 格式化时间
const formatCurrentTime = () => {
  const now = new Date();
  const year = now.getFullYear();
  const month = (now.getMonth() + 1).toString().padStart(2, "0");
  const day = now.getDate().toString().padStart(2, "0");
  const hours = now.getHours().toString().padStart(2, "0");
  const minutes = now.getMinutes().toString().padStart(2, "0");
  const seconds = now.getSeconds().toString().padStart(2, "0");
  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
};

// ===============接入虚拟人

import TransWorker from "./lib/transcode.worker";
import { encode, decode } from "js-base64";
import VMS from "./lib/vms-2d-web-sdk-2.0.0.esm.min.js";

const transWorker = new Worker(TransWorker);
import {
  VAPPID as appId,
  VAPI_SECRET as apiSecret,
  VAPI_KEY as apiKey,
} from "@/utils/APIKEY";

console.log(appId);
console.log(apiSecret);
console.log(apiKey);
//形象列表
const avatarList = {
  110017006: {
    name: "马可",
    actionList: [
      {
        name: "身体微侧，左手有请",
        id: "A_LH_please_O",
      }
    ],
  },
  // 110023005: '小柔',
  110018008: {
    name: "晓颜",
    actionList:[]
  },
  110026010: {
    name: "伊凡",
    actionList: []
  },
  118801001: {
    name: "依丹",
    actionList: [
      {
        name: "左手左上介绍",
        id: "A_LH_introduced_O",
      }
    ],
  },
  110022010: {
    name: "晓云",
    actionList: [
      {
        name: "左手左上介绍",
        id: "A_LH_introduced_O",
      }
    ],
  },
  110005018: {
    name: "知新",
    actionList: [
      {
        name: "左手点击",
        id: "A_LH_click_O",
      },
    
    ],
  },
  110017009: {
    name: "110017009",
    actionList: [
      {
        name: "左手左上介绍",
        id: "A_LH_introduced_O",
      },
     
    ],
  },
};
//发音人列表
const vcnList = {
  x3_qianxue: "X3_QIANXUE",
  x3_xiaoye_chongni: "x3_XIAOYE_CHONGNI",
  x3_yezi: "x3_YEZI",
  x3_doudou: "x3_DOUDOU",
  x3_yilin: "x3_YILIN",
  x3_mingge: "x3_MINGGE",
  x3_chaoge: "x3_CHAOGE",
  x3_shaohua: "x3_SHAOHUA",
  x3_lwk_female: "x3_lwk_female",
};

let audioContext, scriptProcessor, mediaSource, webSocket;

let status = "end";

const width = ref(1090);
const height = ref(720);

const avatarId = "110029003";

const audioActions = ref("");
const textPostpro = "";

const transparent = ref(0);
const textActions = ref([
  { type: "action", value: "A_3_7_TYT_M_0", wb: 3, we: 20 },
  { type: "action", value: "A_3_7_ZQQ_M_0", wb: 30, we: 50 },
]);
const actionList = ref([]);

const vmsStatus = ref(0);

const initClient = () => {
  VMS.start({
    appId: appId,
    apiKey: apiKey,
    apiSecret: apiSecret,
    width: width.value, //[1920 1280 720]
    height: height.value, // [1080 720 405]
    avatarId: avatarId,
    streamDomId: "remote_stream",
    resId: "",
    isSsl: true,
    transparent: transparent.value,
    // moveH:moveH,
    // moveV:moveV,
    // scale:scale,
    // maskRegion: maskRegion //'[0,154,1080,1472]'
  })
    .then(() => {
      vmsStatus.value = 1;
      // initNitTextDriver();
    })
    .catch((e) => {
      if (e.stream && e.code === 0x4043) {
        let stream = e.stream;
        console.log("兼容有问题");
      }
      vmsStatus.value = 0;
    });
};

transWorker.onmessage = function (event) {
  if (status !== "end") {
    VMS.audioDriverSendData({
      parameter: {
        vms_dispatch: {
          realtime_status: {
            tts_status: 0,
            vmr_status: 0,
            vmr_action_status: 0,
          },
        },
      },
      payload: {
        audio: {
          audio: [...event.data],
          encoding: "raw",
          sample_rate: 16000,
        },
        ctrl_t: audioActions
          ? {
              encoding: "utf8",
              compress: "raw",
              format: "json",
              text: encode(
                JSON.stringify({
                  avatar: audioActions,
                })
              ),
            }
          : undefined,
        ctrl_postproc: textPostpro
          ? {
              // 字偏移控制指令
              encoding: "utf8",
              compress: "raw",
              format: "json",
              status: 1,
              text: encode(JSON.stringify(textPostpro)),
            }
          : undefined,
      },
    });
    if (audioActions) {
      setAudioActions(undefined);
    }
    if (textPostpro) {
      setTextPostpro(undefined);
    }
  }
};

// setTimeout(()=>{
//     initClient();
// },2000)

//停止服务
const stop = () => {

  // 数据重置
  message.value = "";

  list.value = [
    {
      left: false,
      message: "可以提问啦~",
    },
  ];


  VMS.stop()
    .then(() => {
      // setVmsStatus(0);
      vmsStatus.value = 0;
    })
    .catch((e) => {
      // setVmsStatus(0);
      vmsStatus.value = 0;
    });


};


const textContent = ref("你好啊");

//文本驱动输入框输入
//   声音的id    speed: 50,
let vcn = "x4_lingxiaoshan_profnews";
let speed = 50;
let pitch = 50;
let volume = 50;

let realtimeStatus = 0;
let interactiveMode = 0;
// let  interactiveMode =  0

const textDrive = () => {
  if (!textContent.value) {
    return;
  }

  VMS.textDriver({
    parameter: {
      tts: {
        vcn, // 发音人
        speed, // 语速：[0,100]，默认50
        pitch, // 语调：[0,100]，默认50
        volume,
      },
    },
    payload: {
      text: {
        encoding: "utf8",
        compress: "raw",
        format: "plain",
        status: 3,
        text: encode(textContent.value),
      },
      ctrl_w: textActions.value
        ? {
            // 字偏移控制指令
            encoding: "utf8",
            compress: "raw",
            format: "json",
            status: 3,
            text: encode(
              JSON.stringify({
                avatar: textActions.value,
              })
            ),
          }
        : undefined,
      ctrl_postproc: textPostpro
        ? {
            // 字偏移控制指令
            encoding: "utf8",
            compress: "raw",
            format: "json",
            status: 3,
            text: encode(JSON.stringify(textPostpro)),
          }
        : undefined,
    },
  })
    .then((res) => {
      // debugger;
    })
    .catch((e) => {});
};

function stripMarkdown(text) {
  // 正则表达式匹配常见的Markdown格式
  const markdownPatterns = [
    /#+\s/, // 标题 (# H1, ## H2, etc.)
    /[*+-]/, // 无序列表项 (*, +, -)
    />/, // 引用 (>)
    /`{3}/, // 代码块 (``` )
    /[~_]{1,2}/, // 删除线/下划线 (~~, __, __)
    /[{}]?\[.*?\](.*?)/, // 链接和图片 ([]() and ![])
    /\*\*.*?\*\*/, // 粗体 (**bold**)
    /\*.*?\*/, // 斜体 (*italic*)
    /^1\. /, // 有序列表项 (1.)
    /^ {0,3}>\s/, // 块引用 (> blockquote)
    /^ {0,3}`{3}.*?`{3}$/m, // 代码块 (``` code ``` )
    /!\[.*?\]/, // 图片 (![alt](src)        /\[.*?\]/, // 链接 ([text](url)        /^ {0,3}\d+\. /, // 有序列表项 (1., 2., ...)
    /[\[\]]/, // 去除单独的方括号 ([])
    /[\*]/, // 去除单独的星号 (*)
    /[`]/, // 去除单独的反引号 (`)
    /[~_]/, // 去除单独的删除线/下划线 (~, _)
    /^ {0,3}>/, // 去除块引用的前缀 (> blockquote)
    /^ {0,3}\*{3}/, // 去除粗体的前缀 (***bold***)
    /^ {0,3}\*.*?\*/, // 去除斜体的前缀 (*italic*)
    /^ {0,3}\d+\./, // 去除有序列表项的前缀 (1., 2., ...)
    /^ {0,3}[-*+]/, // 去除无序列表项的前缀 (*, -, +)
    /^ {0,3}#{1,6}\s/, // 去除标题的前缀 (#, ##, ...)
    /[\r\n]{2,}/g, // 连续的换行符
  ];

  // 应用所有模式并返回结果
  return markdownPatterns.reduce(
    (text, pattern) => (pattern.test(text) ? text.replace(pattern, "") : text),
    text
  );
}

let strArr = [];
let i = 0;
let totalI = 5
let strTotal = "";
//初始化流式文本驱动

let  testi = 0



// 初始化流打断

const initStreamtry = () => {
  VMS.textDriverNitInit((res) => {

    if(!res.payload) {
      testi ++

      if(i > 3) return  
      console.error(`output->错误连接再次尝试`,i)
      initStreamtry()
    }


    console.log(
      "非打断返回的实时数据：",
      res.payload ? decode(res.payload.realtime_status.text) : res.payload
    );


 

  })
    .then((res) => {
      console.log("非打断文本驱动建立连接成功啦");
    })
    .catch((e) => {
      // debugger;


    })
}

let arrStrAll = []
let arrI = 0
const initNitTextDriver = () => {
  text.value.push({
    role: "user",
    content: message.value,
  });
  list.value.push({ left: false, message: message.value, tips: true });
  list.value.push({ left: true, message: "", tips: true });



  initStreamtry()

  bigModel.value.start({
    message: message.value,
    fn: (res) => {

   let str = stripMarkdown(res).replace(/^,+|，+|。+|\.+/, '');

   

   arrStrAll.push(str)
   console.log(`output->str`, str)

   arrI++
   console.log(`output->arrStrAll`,arrStrAll,arrI)


   if(!str)  return
   i++
   if(i <= totalI) {
    strTotal +=  str
     console.log(`output->strTotal`,strTotal)
    return  
   }

 
   VMS.sendTextDriverData({
     parameter: {
       tts: {
         vcn, // 发音人
         speed, // 语速：[0,100]，默认50
         pitch, // 语调：[0,100]，默认50
         volume,
       },
       vms_dispatch: {
         realtime_status: {
           tts_status: realtimeStatus,
           vmr_status: realtimeStatus,
           vmr_action_status: realtimeStatus,
         },
         interactive_mode: interactiveMode,
       },
     },
     payload: {
       text: {
         encoding: "utf8",
         compress: "raw",
         format: "plain",
         status: 3,
         text: encode( i === totalI + 1 ? strTotal + str  : str  ),
       },
       ctrl_w: textActions.value
         ? {
             // 字偏移控制指令
             encoding: "utf8",
             compress: "raw",
             format: "json",
             status: 3,
             text: encode(
               JSON.stringify({
                 avatar: textActions.value,
               })
             ),
           }
         : undefined,

       ctrl_postproc: textPostpro
         ? {
             // 字偏移控制指令
             encoding: "utf8",
             compress: "raw",
             format: "json",
             status: 3,
             text: encode(JSON.stringify(textPostpro)),
           }
         : undefined,
     },
   })
     .then((res) => {
       console.log("文本驱动数据发送成功", res);

       ContainerScrollToBottom();

       message.value = ''
     })
     .catch((e) => {
       console.log("文本驱动数据发送失败", e);
       // debugger;

       initStreamtry()
     })
 }
  })
}

//重置
const pauseTextDriverNit = () => {
  VMS.pauseTextDriverNit();
};

//文本驱动非打断
const textDriveNit = () => {
  // 存储机器人回答的消息
  if (message.value.trim().length <= 0)
    return ElMessage.error("消息不能为空哦");

  initNitTextDriver();
};

const dialogTableVisible = ref(false);

const formLabelWidth = "140px";

const showDialog = () => {
  dialogTableVisible.value = true;

  initClient();
};

// 滚动内容到底部
function ContainerScrollToBottom() {
  let element = document.querySelector(".scroll-container");
  let element2 = document.querySelector("#chat-message");
  element.scrollTop = element.scrollHeight;

  element.scrollTo(0, element2.scrollHeight - element.clientHeight);  //最底部


}


let  isShow = ref(false)

nextTick(()=>{
  
// 获取视频元素
var video = document.getElementById('myVideo');
 
 // 为视频添加'ended'事件监听器
 video.addEventListener('ended', function() {
     // 当视频播放完毕时，这里的代码会被执行
     console.log('视频播放已完成！');
     try{  stop() }
     catch(e){}
     finally {
      isShow.value = true
     }
     
 });
 
 // 为视频添加'ended'事件监听器
 video.addEventListener('pause', function() {
     // 当视频播放完毕时，这里的代码会被执行
     console.log('视频暂停了！');
     try{  stop() }
     catch(e){}
     finally {
      isShow.value = true
     }
 });
 // 为视频添加'play'事件监听器
 video.addEventListener('play', function() {
     // 当视频播放完毕时，这里的代码会被执行
     console.log('播放了了！');
     isShow.value = false
 });
})


</script>
<template>
  <el-card class="chat-box" ref="box">
    <!-- <button @click="showDialog">按钮12</button>
    <button @click="stop">停止虚拟人</button> -->
    <img  @click="showDialog" v-if="isShow"  class="avatar2" src="https://img1.baidu.com/it/u=3311638870,3032747726&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500">
    <video ref="video"  id="myVideo" class="video"  controls src="./video/02.mp4"></video>


    <el-dialog
      class="dialog"
      @close="stop"
      v-model="dialogTableVisible"
      title="教学小助手"
      width="80%"
    >
      <el-row>
        <el-col :span="6">
          <!-- <button @click="initClient">启动虚拟人</button>
          <button @click="stop">停止虚拟人</button>
          <button @click="textDrive">开启播报</button>
          <button @click="textDriveNit">流式播报播报</button>
  
          <input type="text" v-model="textContent" /> -->

          <div id="remote_stream" className="remote-stream-area"></div>

          <!-- {/* 视频流展示区域 */} -->
          <div id="local_stream"></div>
        </el-col>
        <el-col :span="18">
          <div class="scroll-container">
            <ul
              id="chat-message"
              class="list-unstyled py-4 scroll-content chat-message"
            >
              <template v-for="(item, index) in list" :key="index">
                <li
                  v-if="!item.left"
                  id="user-chat-question-1"
                  :key="index"
                  class="d-flex message message-user right"
                >
                  <div class="message-body">
                    <div class="message-row message-row-con">
                      <div
                        id="message-user-chat-question-1"
                        class="message-content p-3 text-chat"
                      >
                        {{ item.message }}
                      </div>
                    </div>
                  </div>
                </li>
                <li
                  v-else
                  id="user-chat-question-1"
                  class="d-flex message message-user right left"
                  :style="{
                    display: item.message.length > 0 ? 'block' : 'none',
                  }"
                >
                  <div class="message-body">
                    <span class="date-time text-muted">{{
                      formatCurrentTime()
                    }}</span>
                    <div class="message-row message-row-con">
                      <div
                        id="message-user-chat-question-1"
                        class="message-content p-3 text-chat"
                      >
                        <!-- <VueMarkdown :source="item.message"> </VueMarkdown> -->
                        <VMarkdownView
                          :mode="mode"
                          :content="item.message"
                        ></VMarkdownView>
                        <p
                          @click="stopAnswer"
                          :style="{
                            visibility: item.tips ? 'visible' : 'hidden',
                          }"
                          class="answers"
                        >
                          停止回答
                        </p>
                      </div>
                    </div>
                  </div>
                </li>
              </template>
            </ul>

            <el-card class="chat-footer">
              <div class="inp">
                <input
                  v-model="message"
                  placeholder="请输入内容"
                  type="text"
                  @keyup.enter="textDriveNit"
                />
                <button :disabled="disabled" @click="textDriveNit">
                  {{ disabled ? "回答中..." : "发送" }}
                </button>
              </div>
            </el-card>
          </div>
        </el-col>
      </el-row>
    </el-dialog>
  </el-card>
</template>
    

<style scoped   lang="scss">
li {
  list-style: none;
}

.py-4 {
  padding-top: 1.5rem !important;
  padding-bottom: 1.5rem !important;
}

.message.right {
  text-align: right;
}

.chat-content .message + .message {
  margin-top: 10px;
}

.avatar1,
.avatar {
  width: 60px;
  border-radius: 50% !important;
}

.p-2 {
  padding: 0.5rem !important;
}

.message .avatar {
  border: 2px solid #21aa93;
  border-radius: 50% !important;
}

.text-muted {
  color: #95aac9;
}

.message.right .message-content {
  background-color: #f5f5f5;
  text-align: left;
  border-radius: 15px 0 15px 15px;
}

.message .message-content {
  max-width: 80%;
  min-width: 10%;
  border-radius: 0 15px 15px 15px;
  font-size: 16px;
  line-height: 1.6;
  padding-bottom: 40px;
}

.right .message-row-con {
  display: flex;
  justify-content: flex-end;
}

.right .message-content {
  padding: 10px;
  margin-top: 5px;
}

.d-flex {
  margin-top: 20px;
}

.message.right.left {
  text-align: left;
}

.message.right.left .message-row {
  justify-content: flex-start;
}

.message.left .message-content[data-v-73c72fc6] {
  background-color: #f5f5f5;
  text-align: left;
  border-radius: 0 15px 15px 15px;
}

.chat-footer {
  background-color: #fff !important;
  padding: 0 20px;
  position: absolute;
  width: calc(100% - 350px);

  bottom: 0;
  right: 0;
 
  margin: 20px 0;
  display: flex;
  background-color: var(--body-color);
  padding-top: 0.5rem !important;

  border-top: 1px solid #dee2e6;

  ::v-deep .el-card__body {
    flex: 1;
  }
}
.inp {
  display: flex;
}
.chat-footer input {
  flex: 1;
  border: none;
  outline: none;
  height: 40px;
}
.scroll-container {
  width: 100%;
  height: 400px;
  overflow: auto;
}
.chat-footer button {
  background-color: #21aa93;
  border-color: #21aa93;
  color: #fff;
  width: 90px;
  height: 40px;
  border: 0;
  outline: none;
  cursor: pointer;
}

.list-unstyled {
  margin-bottom: 50px;
}

.chat-box {
  padding: 10px;
  
  width: 100vw;
  height: 100vh;
  box-sizing: border-box;

  ::v-deep .el-card__body {
    height: 100%;

    .video {
      width: 100%;
      height: 100%;
      object-fit: cover;
    }
  }
}
.answers {
  transform: scale(0.9);
  font-size: 12px;
  background: #f5f5f5;
  width: 61px;
  padding: 2px 5px;
  text-align: center;
  border-radius: 26px 0;
  cursor: pointer;
}

.markdown-body {
  padding: 8px;
}

#remote_stream {
  width: 80%;
}

::v-deep .dialog {
  background: url(./images/01.jpeg) no-repeat top center/cover;
}




.avatar2 {
  position: fixed;
  right:  0;
  bottom: 220px;
  width: 50px;
  height: 50px;
  border-radius: 50%;
  z-index: 1000;
}
</style>
